#ifndef GTL_STRINGS_HEADER_GUARD
#define GTL_STRINGS_HEADER_GUARD

#include <eastl/string.h>
#include <eastl/vector.h>
#include <sstream>
//#include <sstream>
//#include <cctype>
//#include <vector>

#include "gtlCompilerSpecific.h"

/// TODO: replace, case-insensitive compare
/// ToString(T obj) i specjalizacje
/// T ParseFromString(string) i specjalizacje
/// string PrintFormattedCStyle()

namespace GTL
{
  namespace Strings
  {
    ///************************************************************************/
    ///* String and wide string operations                                    */
    ///************************************************************************/

		///************************************************************************/
		/// Conversions
		///************************************************************************/

    /// W16 is endian-agnostic UCS-2, NOT UTF-16; that is, the endianness is machine specific,
    /// and surrogate pairs are not supported

    inline int A8StringToInteger(const char* str, bool* success = 0);
    inline int W16StringToInteger(const wchar_t* str, bool* success = 0);
    
    inline float A8StringToFloat(const char* str, bool* success = 0);
    inline float W16StringToFloat(const wchar_t* str, bool* success = 0);

    inline char* IntegerToA8String(char* buf, int64_t val, size_t radix = 10)
    {
      char *p;
      char *firstdig;
      char temp;
      unsigned int digval;

      p = buf;

      if (val < 0)
      {
        // Negative, so output '-' and negate
        *p++ = '-';
        val = -val;
      }

      // Save pointer to first digit
      firstdig = p;

      do 
      {
        digval = (unsigned int) (val % radix);
        val /= radix;

        // Convert to ascii and store
        if (digval > 9)
          *p++ = (char) (digval - 10 + 'a');
        else
          *p++ = (char) (digval + '0');
      } while (val > 0);

      // We now have the digit of the number in the buffer, but in reverse
      // order.  Thus we reverse them now.

      char* result = p;

      *p-- = '\0';
      do 
      {
        temp = *p;
        *p = *firstdig;
        *firstdig = temp;
        p--;
        firstdig++;
      } while (firstdig < p);

      return result;
      /*
      itoa(value, dest_string, base);
      return dest_string; /// TODO: FIXME: powinno zwracac znak po ostatnim przerobionym znaku!!!!*/
    }
    inline wchar_t* IntegerToW16String(wchar_t* dest_string, long long int value, size_t base = 10);

    inline wchar_t* W16StringPrintFormatted(const wchar_t* format, wchar_t* dest_string, ...);
    inline uint32_t W16StringLength(const wchar_t* str);
    inline int W16StringCompare(const wchar_t* str1, const wchar_t* str2);

    inline void A8StringToW16String(const char* str, wchar_t* outstr);
    inline void W16StringToA8String(const wchar_t* str, char* outstr);

    inline void W16StringToUTF8String(const wchar_t* str, char* outstr);
    inline void UTF8StringToW16String(const char* str, wchar_t* outstr);
    inline void W16StringLengthWhenUTF8(const wchar_t* str);

    typedef char* A8String;
    typedef wchar_t* W16String;

		///************************************************************************/
		/// Trims
		///************************************************************************/

		inline eastl::string TrimLeft(const eastl::string& other, const char* characters = " \t\r\n\v\f")
    {
      size_t i = other.find_first_not_of(characters);
      if (i == eastl::string::npos) return "";
      return other.substr(i);
    }
    inline eastl::string TrimRight(const eastl::string& other, const char* characters = " \t\r\n\v\f")
    {
      size_t i = other.find_last_not_of(characters);
      if (i == eastl::string::npos) return "";
      return other.substr(0, i);
    }
    inline eastl::string Trim(const eastl::string& other, const char* characters = " \t\r\n\v\f")
    {
      size_t first = other.find_first_not_of(characters);
      if (first == eastl::string::npos) return "";
      size_t last = other.find_last_not_of(characters);
      return other.substr(first, (last-first)+1);
    }

    template <typename FUNC> inline eastl::string TrimByFunctionLeft(const eastl::string& str, FUNC func);
    template <typename FUNC> inline const char* TrimByFunctionLeft(const char* str, FUNC func);
    template <typename FUNC> inline eastl::string TrimByFunctionRight(const eastl::string& str, FUNC func);
    template <typename FUNC> inline const char* TrimByFunctionRight(const char* str, FUNC func);
    template <typename FUNC> inline eastl::string TrimByFunction(const eastl::string& str, FUNC func);
    template <typename FUNC> inline const char* TrimByFunction(const char* str, FUNC func);

		///************************************************************************/
		/// UTF-8 utilities
		///************************************************************************/

		namespace UTF8
		{
			/// Encodes the codepoint into `out_buf`, writes at most 4 bytes
			size_t Encode(int codepoint, char* out_buf);
			
			/// Returns how many bytes would it take to encode the codepoint
			size_t ByteWidth(int codepoint);
			
			/// Returns how many bytes make up the codepoint at `utfbuf`
			size_t ByteWidth(const char* utfbuf);
			
			/// Returns the offset in bytes to the character at `index`
			size_t GetOffset(const char* buf, size_t index);
			
			/// Returns the number of UTF-8 characters in the string
			size_t GetLength(const char* buf);
			
			/// Returns the UTF-8 codepoint at character index
			int Get(const char* buf, int index);
			
			/// Returns the codepoint at `buf` and stores the pointer to the next
			/// codepoint in `out`. Returns how many bytes the pointer moved.
			int Next(const char* buf, const char** out);

			/// Returns the codepoint that ends before `buf` and stores the pointer to that
			/// codepoint in `out`. Returns how many bytes the pointer moved.
			int Prev(const char* buf, const char** out);

			/// Returns the UTF-8 representation of the wide string in `str`
			eastl::string WideCharToUTF8(const wchar_t* str);
		}

		///************************************************************************/
		/// Compare
		///************************************************************************/

		inline bool __inteq(int a, int b) { return a==b; }
		inline bool __intneq(int a, int b) { return a!=b; }

		template <typename T1, typename T2>
		bool WildcardCompare(const T1 *wild, const T2 *string) {
			// Written by Jack Handy; templated by me :)
			const T1 *mp = NULL;
			const T2 *cp = NULL;

			while ((*string) && (*wild != '*')) {
				if (__intneq(*wild, *string) && (*wild != '?'))
					return false;
				wild++;
				string++;
			}

			while (*string) {
				if (*wild == '*') {
					if (!*++wild)
						return true;
					mp = wild;
					cp = string+1;
				} else if (__inteq(*wild, *string) || (*wild == '?')) {
					wild++;
					string++;
				} else {
					wild = mp;
					string = cp++;
				}
			}

			while (*wild == '*')
				wild++;
			return !*wild;
		}

		///************************************************************************/
		/// Replace
		///************************************************************************/

		inline eastl::string& Replace(eastl::string& str, const char* from, const char* to)
		{
			size_t start_pos = 0;
			size_t from_length = strlen(from);
			size_t to_length = strlen(to);
			while((start_pos = str.find(from, start_pos)) != std::string::npos) {
				str.replace(start_pos, from_length, to);
				start_pos += to_length; // In case 'to' contains 'from', like replacing 'x' with 'yx'
			}
			return str;
		}

		inline eastl::string AddSlashes(const eastl::string& s, const char* beforechars = "\\\"'")
		{
			eastl::string str = s;
			size_t start_pos = 0;
			char repl[3] = "\\0";
			while((start_pos = str.find_first_of(beforechars, start_pos)) != std::string::npos) {
				repl[1] = str[start_pos];
				str.replace(start_pos, 1, repl);
				start_pos += 2;
			}
			return str;
		}

		///************************************************************************/
		/// Transforms
		///************************************************************************/
		
		/// We use this instead of std::transform because this actually casts the argument and result of the transform function
		/// to char
		template <typename T>
		eastl::string Transform(const eastl::string& other, T(*func)(T))
		{
			eastl::string result = other;
			size_t size = result.size();
			for (size_t i = 0; i<size; ++i)
				result[i] = (char)func((char)result[i]);
			return result;
		}
		inline eastl::string ToUpper(const eastl::string& other) { return Transform<int>(other, ::toupper); }
		inline eastl::string ToLower(const eastl::string& other) { return Transform<int>(other, ::tolower); }

    template <typename T>
    inline int AlwaysOne(T other) { return 1; }

    template <typename WIDTH_FUNC>
    inline eastl::string WrapDontCutWords(const eastl::string& str, size_t maxWidth, char wrapChar = '\n', WIDTH_FUNC getWidthFunc = AlwaysOne<char>)
    {
      return getWidthFunc(wrapChar)?"":"a";
      /*
      laststart = lastspace = 0;
      for (current = 0; current < textlen; current++) {
        if (text[current] == breakchar[0]) {
          laststart = lastspace = current + 1;
        } else if (text[current] == ' ') {
          if (current - laststart >= linelength) {
            newtext[current] = breakchar[0];
            laststart = current + 1;
          }
          lastspace = current;
        } else if (current - laststart >= linelength && laststart != lastspace) {
          newtext[lastspace] = breakchar[0];
          laststart = lastspace + 1;
        }
      }*/
    }

    template <typename WIDTH_FUNC>
    inline eastl::wstring WrapDontCutWords(const eastl::wstring& str, size_t maxWidth, wchar_t wrapChar = L'\n', WIDTH_FUNC getWidthFunc = AlwaysOne<wchar_t>)
    {
      return L"";
    }

		///************************************************************************/
		/// Tokenize
		///************************************************************************/

    void Tokenize(const eastl::string& str, eastl::vector<eastl::string>& output, const char* tokens = " ", const char* quote_tokens = "");
    void Tokenize(const eastl::wstring& str, eastl::vector<eastl::wstring>& output, const wchar_t* tokens = L" ", const wchar_t* quote_tokens = L"");
    void Tokenize(const char* str, eastl::vector<eastl::string>& output, const char* tokens = " ", const char* quote_tokens = "");
    void Tokenize(const wchar_t* str, eastl::vector<eastl::wstring>& output, const wchar_t* tokens = L" ", const wchar_t* quote_tokens = L"");

		///************************************************************************/
		/// Split
		///************************************************************************/

    /// Function: SplitSingle
    /// Finds the first occurence of `on` in `str` and returns what's after
    inline eastl::string SplitSingle(const eastl::string& str, const eastl::string& on)
		{
			size_t pos = str.find(on);
			if (pos == eastl::string::npos) return "";
			return str.substr(pos+on.size());
		}
    /// Function: SplitMultiple
    /// Splits the string using `on` and stores the results in `result`. Does not clear `result` on start.
    inline void SplitMultiple(const eastl::string& str, const eastl::string& on, eastl::vector<eastl::string>& result)
		{
			size_t pos = 0, last_pos = 0;
			do 
			{
				pos = str.find(on, last_pos);
				result.push_back(str.substr(last_pos, pos-last_pos));
				last_pos = pos+on.size();
			} while (pos != eastl::string::npos);
		}

    eastl::string Join(const eastl::vector<eastl::string>& strings, const char* delimiter = " ", bool ignoreEmpty = true);

		struct StringCompareFunctor
		{
			bool operator()(const char* s1, const char* s2) const
			{
				return strcmp(s1, s2) < 0;
			}
			bool operator()(const char* s1, const eastl::string& s2) const
			{
				return s1 < s2;
			}
			bool operator()(const eastl::string& s1, const char* s2) const
			{
				return s1 < s2;
			}
		};

    ///************************************************************************/
    ///* Format functions                                                     */
    ///************************************************************************/

		inline eastl::string Printf(const char* fmt, ...) 
		{
			int size=100;
			eastl::string str;
			va_list ap;

			for(;;)
			{
				str.resize(size);
				va_start(ap, fmt);
				int n = vsnprintf((char *)str.c_str(), size, fmt, ap);
				va_end(ap);
				if (n > -1 && n < size)
					return str;
				if (n > -1)
					size=n+1;
				else
					size*=2;
			}
		}

    #include "gtlStrings_Format.inl"

    /// Basically:
    /// 
    ///   template <typename SINK_TYPE, typename FORMAT_TYPE, ...>
    ///   void Format(SINK_TYPE& sink, FORMAT_TYPE format, ...)
    /// 
    /// Usage:
    /// 
    ///   char charBuf[50];
    ///   Format(charBuf, "hello {0}", "world");
    /// 
    ///   or
    /// 
    ///   string buf, ble = "hello {1}{0}";
    ///   Format(buf, ble, 14.0f, "asd");


    ///************************************************************************/
    ///* Base64                                                               */
    ///************************************************************************/
    void Base64Encode(const char* buffer, char* outbuf);
    void Base64Decode(const char* buffer, char* outbuf);

    ///************************************************************************/
    ///* RLE compression                                                      */
    ///************************************************************************/

    /// Algorithm description:
    ///   While input left:
    ///     If between 4 and 2^15 successive equal bytes, output the number of bytes as a uint16, and the byte as uint8
    ///     Else, store the current position in input as "start", find the next 4+ equal bytes (or 2^15 bytes since start, or end of input)
    ///       and store that postiion as "end"; calculate "end"-"start", negate it and output as uint16; then, output
    ///       the bytes between "start" and "end"

    inline size_t RLECompress(const char* in, size_t inlen, char* out);
    inline size_t RLECalculateCompressedSize(const char* in, size_t inlen, char* out);
    inline void ForEachRLECompressedByte(const char* buf, size_t size, void(*func)(char,size_t,void*), void* userdata);
    inline size_t RLECalculateDecompressedSize(const char* in, size_t inlen);
    inline size_t RLEDecompress(const char* in, size_t inlen, char* out);
    inline void DeltaEncodeInPlace(char* buf, size_t size);

    #include "gtlStrings_RLE.inl"

		namespace Just
		{
			inline eastl::string IntToString(int i)
			{
				char tempBuf[32];
				IntegerToA8String(tempBuf, (int64_t)i, 10);
				return tempBuf;
			}
			inline int StringToInt(const char* str)
			{
				return atoi(str);
			}
			inline int StringToInt(const eastl::string& str)
			{
				return atoi(str.c_str());
			}
		}

		
	}
}

#endif